/*
 * Author: Roy Stegers & Gino Biervliet
 *
 * This handles clients and makes threads.
 *
 */


#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <string.h>
#include <semaphore.h>
#include "hfile.h"

//prototype
void *mainThread(void *arg);

//Shared buffer for mmap
char *cpDataBuffer;//Buffer for sending data

//create semaphore for mMap
sem_t *semComChanPost;
sem_t *semComChanRead;

clientInfo client[50];  //Create a array with client structs so that we can use it to put in created semaphores, clientMmap and clientNo for this threath.



/*
 *
 * MAIN PART OF CODE
 *
 */
int main()
{
    pthread_t threadID;//thread id
    //Client handler variables

    int iServersock, iClientSock, iFd;// file descriptors

    char cBuffer[CHUNKSIZE]; //buffer string
    char cClientDesc[10]; //string which contains the fd to concat to the NEWCLIENT String
    char *pMap;//pointer to mmf
    struct sockaddr_in server_struct, client_struct;//structs for connection
    int iYes = 1;//int for reuse addr

    //Remove existing semaphores from list
    if(sem_unlink("semComChanPost") <0)
    {
        perror("unlinksemComChanPost");
    }
    if(sem_unlink("semComChanRead") <0)
    {
        perror("unlinksemComChanRead");
    }

    //create semComChanPost for pre-thread(not pre threading) client server communication
    if((semComChanPost = sem_open("semComChanPost", O_CREAT, ACC_RIGHT, 0)) == SEM_FAILED)
    {
        perror("Open semComChanPost");
        if(sem_unlink("semComChanPost") <0)
        {
            perror("Unlink at error semComChanPost");
        }
    }

    //create semComChanread for pre-thread(not pre threading) client server communication
    if((semComChanRead = sem_open("semComChanRead", O_CREAT, ACC_RIGHT, 0)) == SEM_FAILED)
    {
        perror("Open semComChanRead");
        if(sem_unlink("semComChanRead") <0)
        {
            perror("Unlink at error semComChanRead");
        }
    }

    //Make listen socket
    if ((iServersock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
    {
        perror("Failed to create socket");
    }

    //Fill socket with family, incomend ip en port
    memset(&server_struct, 0, sizeof(server_struct));       //cleans memory
    server_struct.sin_family = AF_INET;                  //Family
    server_struct.sin_addr.s_addr = htonl(INADDR_ANY);   //Ip that is used to listen for incomming connections
    server_struct.sin_port = htons(PORT_NUMBER);                //Port

    //re use addr, setsockopt(set socket options)
   if (setsockopt(iServersock, SOL_SOCKET, SO_REUSEADDR, &iYes,sizeof(int)) < 0)
   {
        perror("setsockopt");
        exit(EXIT_FAILURE);
   }

   //Bind server socket with struct defined above
   if (bind(iServersock, (struct sockaddr *) &server_struct,sizeof(server_struct)) < 0)
   {
        perror("Failed to bind the server socket");
   }

   //Listen mode with max conctions
    if (listen(iServersock, MAXPENDING) < 0)
    {
        perror("Failed to listen on server socket");
    }


    printf("Maak memmap comchan\n");//debug

    //create and open mmap
    if ((iFd = open(FILEPATH_COMMCHAN, O_RDWR | O_CREAT | O_TRUNC, ACC_RIGHT))== -1)
    {
        perror("Error opening file for writing");
        exit(EXIT_FAILURE);
    }
       printf("fd controle: %d\n", iFd);

   
    if ((pMap = mmap(0, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, iFd, 0))== MAP_FAILED)
    {
	close(iFd);
	perror("Error mmapping the file");
	exit(EXIT_FAILURE);
    }

       //Need to be posted for the fist time(for first client connect), the first time we dont get a read from FSP
       if(sem_post(semComChanRead)< 0)
       {
           perror("post error\n");
       }


       printf("File server draait\n");//debug

   //Main infinite loop
   while (1)
   {
        int i = 0;
        unsigned int uiClientlen = sizeof(client_struct);
        //Empty buffer.
        bzero(cBuffer,CHUNKSIZE);
        lseek(iFd, 0, SEEK_SET);
        write(iFd, '\0', CHUNKSIZE);

        if ((iClientSock = accept(iServersock, (struct sockaddr *) &client_struct,&uiClientlen)) < 0)
        {
            perror("Failed to accept client connection");
        }
       //Make client thread
        pthread_create(&threadID, NULL, mainThread, (void *)iClientSock);

        //make newclient message(NEWCLIENT + cClientDesc) to let the FSP know that a new client is connecting
        sprintf(cClientDesc,"%d\n",iClientSock);
        printf("sprinf\n");     //Debug line
        strcpy(cBuffer,"NEWCLIENT ");
        printf("strcpy\n");     //Debug line
        strcat(cBuffer, cClientDesc);
        printf("strcat\n");     //Debug line

        printf("cBuffer = %s\nfd = %d\n", cBuffer, iFd);     //Debug line

        printf("waiting for semComChanRead\n");
      
        //Make sure that the comChanMmap is read by the client. So that a next client doesn't write new data over mmap.
        if(sem_wait(semComChanRead)<0)
        {
            perror("sem wait failt");
        }

        //write to MMF to let FSP know that there is a new client
        if(write(iFd, cBuffer, strlen(cBuffer)) < 0)
        {
            perror("write newclient");
        }

        printf("pMap = %s\n", pMap);            //Debug line
        printf("Write cBuffer Neweclient\n");   //Debug line

        //Post to let FSP know the MMF is ready to read
        if(sem_post(semComChanPost) <0)
        {
            perror("send failed");
        }
        printf("postComChanPost succes\n");     //Debug line


   }
}


//Function that will be called For every incomming client.
void *mainThread(void *arg)
{
    int iClientSock = (int)arg;//file descriptor of client that has been given to thread from main routine
    int iReceived; //used for couning received.

    //used to make string for MMF paths
    char cCFD[10], cBuffer[CHUNKSIZE];
    char *cFilePath = {"/tmp/"};
    char *cExtention = {".bin"};
    char cCFDFull[10], cCFDEmpty[10];
    
    char* cFull;
    char* cEmpty;
  

    client[iClientSock].clientNo = iClientSock;

    printf("Client conected with number: %d\n", iClientSock);//Debug

    //fillpath(/tmp/ + clientfd + .bin)

    //place iClientSock in string
    sprintf(cCFD,"%d",iClientSock);

    printf("Before create mmf path\n");//Debug
    //create mmf path, reserve memory to put data in.
    client[iClientSock].cMemMapPath = malloc(1000);
    //Put /tmp/ in CmemMapPath
    strcat(client[iClientSock].cMemMapPath, cFilePath);
    printf("Before create mmf path  2\n");
    //Put client fd cMemMapPath
    strcat(client[iClientSock].cMemMapPath, cCFD);
    printf("Before create mmf path  3\n");
    //Put extention in cMemMapPath
    strcat(client[iClientSock].cMemMapPath, cExtention);

    printf("client mmf filepath = %s\n", client[iClientSock].cMemMapPath);//Debug
    printf("end create mmf path\n");//Debug

    /*Give clientfd to semaphore vars*/

    //format value of int and put it in string.
    sprintf(cCFDFull,"%d",iClientSock);
    //Create semname with sting value and Full(clientfd + Full) to create a unique name with fd + Full
    cFull = strcat((char*) cCFDFull, "Full");

    //format value of int and put it in string.
    sprintf(cCFDEmpty,"%d",iClientSock);
    //Create semname with sting value and Full(clientfd + Empty) to crate a unique name with fd + Empty
    cEmpty = strcat((char*) cCFDEmpty, "Empty");

    printf("cFull: %s\n", cFull);       //Debug line
    printf("cEmpty: %s\n", cEmpty);     //Debug line

    //Unlink just incase there are still sems in the list.
    if(sem_unlink(cFull) < 0)
    {
        perror("cFull unlink\n");
    }
    if(sem_unlink(cEmpty) < 0)
    {
        perror("cEmpty unlink\n");
    }

    //open semFull and semEmpty

    //Create semFull and  put is in struct client[iClientSock]
    if((client[iClientSock].semaphoreFull = sem_open(cFull, O_CREAT, 0600, 0)) == SEM_FAILED)
    {
        perror("Open semFull");
        if(sem_unlink(cFull) <0)
        {
            perror("Unlink at semFull");
        }
    }

    //Create semEmpty and put is in struct client[iClientSock]
    if((client[iClientSock].semaphoreEmpty = sem_open(cEmpty, O_CREAT, 0600, 0)) == SEM_FAILED)
    {
        perror("Open semEmpty");
        if(sem_unlink(cFull) <0)
        {
            perror("Unlink at semEmpty");
        }
    }

    //Create client mmap, create file for mmap
    if ((client[iClientSock].iMapFd= open(client[iClientSock].cMemMapPath, O_RDWR | O_CREAT | O_TRUNC, 0600)) < 0)
    {
        perror("Error opening file for writing");
        exit(EXIT_FAILURE);
    }

    printf("imapfd is : %d\n", client[iClientSock].iMapFd);    //Debug line

    //Map client mmap, put mmap pointer in client[IClientSock] struct, share changes with other proces(FSP)
    if ((client[iClientSock].cMapFile = mmap(0, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, client[iClientSock].iMapFd, 0)) == MAP_FAILED)
    {
	close(client[iClientSock].iMapFd);
	perror("Error mmapping the file");
	exit(EXIT_FAILURE);
    }

        printf("createsemmmap succes\n");   //Debug line

        while(1)    //Command infinite loop
        {
            printf("in while loop\n");     //Debug line

            if(sem_wait(client[iClientSock].semaphoreEmpty))    //Wait for semEmpty,unique(combonation of semaphore and clientfd) for this thread.
            {
                perror("wait semEmpty\n");
            }
            printf("wait for semEmpty complete\n"); //Debug line

            //Wait until message is recived
            if ((iReceived = recv(iClientSock, cBuffer, CHUNKSIZE, 0)) < 0)
            {
                 perror("Ontvangen van data mislukt");
            }

            //Command analyzer if return value is 2 Quit (future)
            if(commandAna(client[iClientSock].clientNo, cBuffer, client) == 2)
            {
                pthread_exit(0);//exit thread
            }
        }
   pthread_exit(0);          //Exit thread
}